/**
    dirtree.c - A collection of routines for constructing a tree of the
    virtual filesystem.
    Copyright (C) 2009  Abram Magner

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "common.h"
#include "dirtree.h"
#include "path.h"
#include "strfuncs.h"


static void free_tree_node (tree_node *);
static void free_subtree (tree_node *);

//=============================================================================
// Create an empty tree.
//=============================================================================
fs_tree *new_tree (void)
{
  fs_tree *retval = malloc (sizeof (fs_tree));
  if (retval == NULL) return (NULL);
  retval->root = NULL;
  return (retval);
}

//=============================================================================
// Free the given tree.
//=============================================================================
void free_tree (fs_tree *tr)
{
  free_subtree (tr->root);
  free (tr);
}

//=============================================================================
// Clean up the given subtree.
//=============================================================================
static void free_subtree (tree_node *root)
{
  list_node *iter = NULL;

  // First base case.
  if (root == NULL) return;
  iter = list_get_iter (root->children);

  while (iter != NULL && list_iter_hasmore (iter))
    {
      free_subtree ((tree_node *)iter->data);
      list_iter_advance (iter);
    }
  free_tree_node (root);
}

//=============================================================================
// Tree node constructor for use in the other tree functions.
//=============================================================================
tree_node *new_tree_node (char *name, unsigned char flags, tree_node *parent)
{
  tree_node *retval = NULL;

  if ((retval = malloc (sizeof (tree_node))) == NULL) return (NULL);
  if ((retval->name = strdup (name)) == NULL)
    {
      free (retval);
      return (NULL);
    }
  if ((retval->children = new_list ()) == NULL)
    {
      free (retval->name);
      free (retval);
      return (NULL);
    }
  retval->flags = flags;
  retval->parent = parent;
  return (retval);
}

//=============================================================================
// Free this node, assuming all of its children have been freed.
//=============================================================================
static void free_tree_node (tree_node *node)
{
  free (node->name);
  free_list (node->children);
  free (node);
}

//=============================================================================
// Return the path corresponding to the given tree node in a newly allocated
// string.
//=============================================================================
char *tree_getpathname (tree_node *curnode)
{
  // TODO: Implement me!
//  fprintf (stderr, "tree_getpathname: Not yet implemented!\n");
  return (NULL);
}

//=============================================================================
// Return the node corresponding to the given path in the tree.
//=============================================================================
tree_node *tree_lookup (fs_tree *tree, char *path)
{
  char *curpath = NULL;
  tree_node *curnode = NULL;

  curnode = tree->root;
  curpath = strdup (path);
  if (curpath == NULL) return (NULL);

  while (strcmp (curpath, "") && curnode != NULL)
    {
      char *temp_ptr = NULL;
      char *curname = NULL;
      curname = path_root (curpath);
      if (curname == NULL)
	{
	  free (curpath);
	  return (NULL);
	}

      curnode = tree_getchild (curnode, curname);
      free (curname);

      temp_ptr = path_next_root (curpath);
      free (curpath);
      if (temp_ptr == NULL) return (NULL);
      curpath = temp_ptr;
    }
  free (curpath);
  return (curnode);
}

//=============================================================================
// Insert the nodes representing the given path into the tree.  The leaf node
// has the given flags.
//=============================================================================
int tree_insertpath (tree_node *tree, char *path, unsigned char flags)
{
  char *curpath = NULL;
  char *abspath = NULL;			// For determining reality of the cur dir.
  tree_node *tnode = NULL;

  tnode = tree;
  curpath = strdup (path);
  if (curpath == NULL) return (-1);
  abspath = malloc (strlen (mount_path) + strlen (path) + 3);
  if (abspath == NULL)
    {
      free (curpath);
      return (-1);
    }
  strcpy (abspath, mount_path);
  if (abspath[strlen (abspath) - 1] == '/') abspath[strlen (abspath) - 1] = '\0';

  while (strcmp (curpath, "") && tnode != NULL)
    {
      tree_node *temp_node = NULL;
      char *curname = NULL, *scrap = NULL;
      unsigned char curflag = 0;
      curname = path_root (curpath);
      if (curname == NULL)
	{
	  free (curpath);
	  return (-1);
	}
      // Update the absolute path and fill in curflag.
      strcat (abspath,  "/");
      strcat (abspath, curname);
      if (path_exists (abspath)) curflag |= REAL_FLAG;
      curflag |= DIR_FLAG;			// Since this is not necessarily a leaf.

      // Get the child of the current node with the current name.
      temp_node = tree_getchild (tnode, curname);
      // Time for a new child.
      if (temp_node == NULL)
	{
          temp_node = tree_addchild (tnode, curname, curflag);
	  if (temp_node == NULL)
	    {
	      free (curpath);
	      free (curname);
	      free (abspath);
	      return (-1);
	    }
	}
      free (curname);

      // Go down a level and advance in the path.
      tnode = temp_node;
      scrap = path_next_root (curpath);
      free (curpath);
      if (scrap == NULL) return (-1);
      curpath = scrap;

    }
  free (curpath);
  if (tnode == NULL) return (-1);
  tnode->flags = flags;
  return (0);
}

//=============================================================================
// Add a child with the given name and flags to the given parent, provided the
// parent is a directory.  Return the new child on success and NULL on failure.
//=============================================================================
tree_node *tree_addchild (tree_node *parent, char *name, unsigned char flags)
{
  tree_node *child = NULL;
  if (parent == NULL || !(parent->flags & DIR_FLAG)) return (NULL);
  child = new_tree_node (name, flags, parent);
  if (child == NULL) return (NULL);
  list_insert (parent->children, parent->children->length, child);
//  fprintf (stderr, "Adding child %s to parent %s with flags %d\n", name, parent->name, flags);
  return (child);
}

//=============================================================================
// Return the child of curnode with the given name.
//=============================================================================
tree_node *tree_getchild (tree_node *curnode, char *name)
{
  list_node *iter = NULL;
  tree_node *child = NULL;

  iter = list_get_iter (curnode->children);
  while (list_iter_hasmore (iter))
    {
      child = iter->data;
      if (!strcmp (child->name, name)) return (child);
      list_iter_advance (iter);
    }
  return (NULL);
}

//=============================================================================
//=============================================================================
tree_node *tree_getparent (tree_node *curnode)
{
  return (curnode->parent);
}

//=============================================================================
// Translate the woofs path of the given node to the real path.
//=============================================================================
char *tree_get_real_path (tree_node *node, char *src_prefix)
{
  char *suffix = NULL, *tempy = NULL;

  while (node != NULL)
    {
      char *tempstr = NULL, *tempstr2 = NULL;
      if (node->flags & REAL_FLAG)
	{
	  if (node->flags & DIR_FLAG) tempstr = strconcat (node->name, "/");
	  else tempstr = strdup (node->name);
	  if (tempstr == NULL)
	    {
	      if (suffix != NULL) free (suffix);
	      return (NULL);
	    }
          tempstr2 = mstrconcat (tempstr, suffix);
	  if (suffix != NULL) free (suffix);
	  if (tempstr2 == NULL) return (NULL);
          suffix = tempstr2;
	}
      node = node->parent;
    }
  if (strlen (src_prefix) == 0 || src_prefix[strlen (src_prefix) - 1] != '/')
    tempy = strconcat (src_prefix, "/");
  else tempy = strdup (src_prefix);
  tempy = mstrconcat (tempy, suffix);
  free (suffix);
  suffix = tempy;
  return (suffix);
}

void tree_print (tree_node *root)
{
  list_node *iter = NULL;
  printf ("Name of this subtree's root: %s\n", root->name);

  iter = list_get_iter (root->children);
  while (list_iter_hasmore (iter))
    {
      tree_node *child = iter->data;
      printf ("Child of %s:\n", root->name);
      tree_print (child);
      list_iter_advance (iter);
    }
}


